<?php

namespace Lib\Core;

class Client {

    private static $class = array();

    public $__config;

    public $__module;

    public $r;

    /**
     * @return static
     */
    public static function instance($item) {
        $className = get_called_class();
        if (empty(self::$class[$className][$item])) {
            self::$class[$className][$item] = new $className($item);
        }
        return self::$class[$className][$item];
    }

    public function __construct($item) {
        $config = new \Config\Client();
        $this->__config = $config->$item;
        $this->r = new ClientRequest($this);
    }

    public function setModule($module) {
        $this->__module = $module;
        return $this;
    }

    public function __get($name) {
        $this->setModule($name);
        return $this->r;
    }

}

class ClientRequest {

    /**
     * @var Client
     */
    protected $client;

    public function __construct($client) {
        $this->client = $client;
    }

    public function __call($func, $params) {
        return $this->__request($this->client->__config, $this->client->__module, $func, $params);
    }

    private function __request($config, $module, $func, $params) {
        $val = json_encode($params);
        $token = md5(md5($config['secret']) . $val);
        $post = array(
            'params' => $params,
            'app' => $config['app'],
            'module' => $module,
            'func' => $func,
            'token' => $token,
        );
        $result = $this->__send($config['address'], $config['port'], $post, 5);
        $data = $this->__parseResult($result, $token);
        return $data;
    }

    private function __parseResult($result, $token) {
        if (!$result) {
            throw new Exception('网络错误');
        }
        $tmp = explode(md5($token), $result);
        $dataType = $tmp[0];
        $string = isset($tmp[1]) ? $tmp[1] : '';
        switch ($dataType) {
            case 'SERIALIZE':
                $obj = unserialize($string);
                $className = get_class($obj);
                if (strpos($className, 'Exception') !== false) {
                    throw $obj;
                }
                return $obj;
            case 'JSON':
                return json_decode($string, true);
            case 'MESSAGEPACK':
                return msgpack_unpack($string);
        }
    }

    private function __send($address, $port, $postData = null, $timeout = 5) {
        $url = 'http://' . $address . ':' . $port;
        $postData['params'] = json_encode($postData['params']);
        $result = \Lib\Util\Comm::request($url, $postData, $timeout);
        return $result;
    }

    /*private function __send($address, $port, $postData = null, $timeout = 5) {
        $client = new \swoole_client(SWOOLE_SOCK_TCP);
        if (!$client->connect($address, $port, $timeout)) {
            throw new Exception('连接失败');
        }
        $client->send(json_encode($postData));
        $result = $client->recv();
        return $result;
    }*/
}

class Exception extends \Exception {

}